Claude Code 又双叒叕开源了!

3 月 31 日,安全研究员 Chaofan Shou 在 npm 上下载了一个再普通不过的包——@anthropic-ai/claude-code。解压之后,他发现了一个不该出现在发布物里的文件:cli.js.map,57MB,里面躺着 4756 个 TypeScript 源文件的完整内容。

Anthropic 自己又忘了在构建流水线里删除 source map。

讽刺的是,这不是第一次了。2025 年 2 月 Claude Code 首发当天,source map 就被人发现过一次,Anthropic 两小时内删除并反向清理了 npm 缓存。但 363 个版本之后,它又回来了。

这份泄漏的源码覆盖了 Claude Code 的客户端逻辑,我花了不少时间通读了社区的逆向分析和源码本身。这篇文章是一份尽可能完整的技术拆解。


一、项目全景:512000 行代码长什么样

先看技术栈。Claude Code 是一个用 TypeScript 写的终端应用,运行在 Bun 上,UI 层用 React + Ink 渲染到终端,CLI 解析用 Commander.js,Schema 验证用 Zod v4,代码搜索引擎是 ripgrep,通过 MCP SDK 和 LSP 协议与外部工具通信。遥测用了 OpenTelemetry + gRPC,Feature Flags 走 GrowthBook,认证体系基于 OAuth 2.0 + JWT + macOS Keychain。

整个 src/ 目录的结构大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
src/
├── main.tsx # 入口:Commander.js CLI 解析 + React/Ink 渲染
├── QueryEngine.ts # 核心 LLM 引擎(约 46000 行)
├── Tool.ts # 工具类型定义(约 29000 行)
├── commands.ts # 命令注册(约 25000 行)
├── tools/ # Agent 工具实现(约 40 个)
├── commands/ # 斜杠命令实现(50+)
├── components/ # Ink UI 组件(约 140 个)
├── services/ # 外部服务集成
├── hooks/ # React Hooks(含权限检查)
├── bridge/ # IDE 集成(VS Code / JetBrains)
├── coordinator/ # 多 Agent 编排
├── plugins/ # 插件系统
├── skills/ # Skill 按需加载
├── server/ # Server 模式
├── remote/ # 远程会话
├── memdir/ # 持久化记忆目录
├── tasks/ # 任务管理
├── state/ # 状态管理
├── voice/ # 语音输入
├── vim/ # Vim 模式
├── buddy/ # 伴侣精灵(彩蛋)
├── schemas/ # 配置 Schema
├── migrations/ # 配置迁移
└── query/ # 查询管线

有几个文件的规模值得单独拎出来说:QueryEngine.ts 大约 46000 行,是整个产品的大脑;Tool.ts 大约 29000 行,定义了所有工具的基础类型和接口;commands.ts 约 25000 行,负责命令注册和执行。仅这三个文件就占了十万行。

项目的启动流程也值得一看。main.tsx 里做了并行预取优化——MDM 配置读取、Keychain 预取在模块加载之前就并行启动,重型依赖比如 OpenTelemetry(约 400KB)和 gRPC(约 700KB)使用 dynamic import 按需加载。用 Bun 的 bun:bundle 做编译时死代码消除,未启用的 Feature Flag 对应的整个模块不会进入最终产物。

这些优化都指向同一个目标:让一个 50 多万行的 TypeScript 项目在终端里秒开。


二、Tool 系统:给 Agent 的”瑞士军刀”

Claude Code 的 Tool 系统大约包含 40 个工具。但真正有趣的不是数量,而是设计哲学。

每个工具都是自包含模块

源码里每个 Tool 都必须实现一组固定接口:

  • 独立的输入 Schema(用 Zod 做运行时验证,类型不对直接拒绝)
  • 独立的权限模型(这个工具需要什么级别的用户确认)
  • 独立的 isReadOnly() 方法(告诉系统自己是否只读)
  • 独立的执行逻辑进度状态

这意味着添加一个新工具不需要改动框架代码——写一个模块,声明好 Schema 和权限级别,注册进去就行。框架层面完全不关心具体工具做什么。

工具分类

从功能上看,这 40 个工具大致分为几类:

文件 I/O:FileReadTool、FileWriteTool、FileEditTool、NotebookEditTool。读写编辑文件和 Jupyter Notebook。FileEditTool 的设计比较讲究——它不是覆盖整个文件,而是要求你提供一段 old_stringnew_string,系统会验证 old_string 在文件中是否唯一出现,只有唯一匹配时才执行替换。这个设计避免了”改了不该改的地方”这类问题。

搜索:GlobTool、GrepTool、WebSearchTool、WebFetchTool。文件匹配走 glob 模式,内容搜索底层是 ripgrep——比 Node.js 原生的文件搜索快一个数量级。

执行:BashTool 是最复杂的单个工具(后面安全部分会详细拆)。另外还有 SkillTool(按需加载知识)、MCPTool(外部工具协议)、LSPTool(语言服务协议)。

Agent 协作:AgentTool(派生子 Agent)、SendMessageTool(Agent 间消息传递)、TeamCreateTool、TaskCreateTool。这是泄漏源码中最令人兴奋的发现之一——Claude Code 已经在源码层面实现了完整的多 Agent 协作框架。

模式控制:ThinkTool(让模型”想一想”但不执行任何操作)、SleepTool(Agent 主动等待)、CronCreateTool(Agent 给自己创建定时任务)。

几个有意思的设计

ToolSearchTool——“搜索工具的工具”。当 MCP 服务器注册了大量外部工具时,不是一次性把所有工具定义塞进上下文,而是让模型通过搜索按需发现可用工具。这个设计直接解决了工具过多时 context window 被撑爆的问题。

SyntheticOutputTool——结构化输出生成。不走自然语言,走 Zod Schema,输出直接可被程序消费。

StickerRequestTool——当用户问 Claude 要贴纸时,会弹出一个终端表单收集收货地址。这是个彩蛋,后面会细说。

设计哲学

一句话概括:工具是 Agent 的手和脚,框架只管”谁能用”和”能不能用”,不管”怎么用”。 模型自己决定什么时候调用什么工具、传什么参数、怎么组合多个工具完成任务。这就是 shareAI-lab 在他们的逆向分析项目里说的那句话——“模型就是 Agent,代码是 Harness”。


三、安全:六层城墙和被 CVE 雕刻出的代码

这是整个源码中最值得细看的部分。Claude Code 的安全设计不是一道防线,而是六层纵深防御。

六层架构

1
2
3
4
5
6
第 1 层:Workspace Trust        —— 打开仓库时的首道关卡
第 2 层:Permission System —— per-tool 粒度的审批
第 3 层:Command Whitelist —— 命令级正则匹配
第 4 层:OS-Level Sandbox —— 操作系统级硬隔离
第 5 层:Network Proxy —— 域名级出站控制
第 6 层:Managed Policy —— 企业远程管理策略

逐层来看。

Workspace Trust 是第一道关。当你 git clone 一个仓库并首次运行 claude 时,如果项目中存在 .claude/settings.json.mcp.jsonCLAUDE.md,系统会弹出信任对话框要求确认。在用户确认之前,所有网络操作被推迟——一个字节都不会发出去。这个机制是在 CVE-2026-21852 之后加的,因为早期版本在 Trust Prompt 弹出之前就已经执行了 API 握手,恶意仓库可以通过 .claude/settings.json 里的 ANTHROPIC_BASE_URL 劫持 API Key。

Permission Systemsrc/hooks/toolPermission/ 下实现。每个工具有自己的权限级别:GlobTool 和 GrepTool 是 always_allow,永远不弹确认;BashTool 非白名单命令是 ask,必须用户点头;被策略禁止的工具是 deny,没有商量余地。用户可以在 settings.json 里自定义 allow/deny 规则,规则匹配的优先级是:精确 deny > 精确 allow > glob deny > glob allow > 默认行为。

Command Whitelist 是 BashTool 独有的一层。源码中维护了大约 80 条正则表达式,每一条对应一个”安全”命令的匹配模式。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// echo: 禁止管道、分号、反引号、子 shell
/^echo\s+(?:'[^']*'|"[^"$<>]*"|[^|;&`$(){}><#\\\s!]+?)*$/

// git diff: 禁止 --ext-diff 和 --extcmd(可执行任意程序)
/^git diff(?!\s+.*--ext-diff)(?!\s+.*--extcmd)[^<>()$`]*$/

// sort: 禁止 -o / --output(可以覆盖任意文件)
/^sort(?!\s+.*-o\b)(?!\s+.*--output)[^<>()$`]*$/

// ps: 禁止 -o(自定义输出格式可泄露进程命令行中的密钥)
/^ps(?!\s+.*-o)[^<>()$`]*$/

// ping: 强制 -c 限次,防止永久 ping
/^ping\s+(?:-c\s+\d+\s+)[^<>()$`]*$/

每条正则都有负向前瞻排除危险参数。sort -o /etc/passwd 能覆盖系统文件,ps -o command 能泄露其他进程的命令行参数(可能包含密码)——这些都被逐个封堵。

OS-Level Sandbox 是第四层,也是最硬的一层。macOS 用 Apple Seatbelt,Linux 用 bubblewrap(bwrap),通过操作系统原语做文件系统和网络的硬隔离。所有子进程继承相同限制——npm install 里的 post-install 脚本也跑在沙箱里。

Network Proxy 在沙箱外部跑一个代理服务器,所有出站请求必须经过它。每次访问新域名都要用户确认。企业可以配置 allowedDomains 白名单。

Managed Policy 是最高优先级——企业安全团队可以通过远程配置锁定所有行为边界,开发者无法绕过。

isXxx 系列:被 CVE 雕刻出来的函数

翻看泄漏源码,你会发现大量以 is 开头的安全判断函数。几乎每一个都对应着一个已知的攻击向量。举几个典型的:

**isPathWithinCwd**——判断路径是否在工作目录内。早期版本用 path.resolve(filePath).startsWith(path.resolve(cwd)),问题是 /home/user/project-evil 也能通过 /home/user/project 的前缀检查。CVE-2025-54794 之后改为加 path.sep 分隔符。

**isDirectoryChangeThenWrite**——检测 cd .claude && echo "malicious" > settings.json 这类组合命令。攻击者先 cd 到受保护目录,再执行写操作。CVE-2026-25722 之后新增的函数。

**isZshClobberBypass**——ZSH 里 >file 等同于 cat > file,可以绕过写保护。GHSA-q728 之后专门加了 ZSH shell 类型检测。

**isFindWithExec**——find . -exec rm -rf {} \; 可以在 find 命令里执行任意操作。GHSA-qgqw 之后被禁止。

**isDomainAllowed**——域名验证。早期版本 evil.github.com 能匹配 github.com 的白名单。GHSA-vhw5 之后改为精确匹配或要求以 . 分隔的子域名。

**hasUnsafeProjectConfig**——检查项目配置文件是否包含危险设置(如覆盖 API endpoint、注册 MCP 服务器、定义 hooks)。GHSA-mmgp 之后新增。

**containsCommandInjection**——检测 Unicode 零宽字符、命令替换 $()、进程替换 <() 等绕过正则的手段。GHSA-xq4m 之后新增。

CVE → 代码修复对照表

CVE / GHSA 严重性 问题 修复函数 修复方式
CVE-2025-54794 High 7.7 路径前缀匹配绕过 isPathWithinCwd path.sep
CVE-2025-54795 High 8.7 echo 命令注入 isCommandWhitelisted 加强 echo 正则
CVE-2026-25722 High 7.7 cd + write 组合绕过 isDirectoryChangeThenWrite 新增检测
GHSA-mhg7 High sed 管道写绕过 isPipedWriteCommand 新增检测
GHSA-ff64 High settings.json 注入逃逸沙箱 isSandboxEscapeAttempt 新增检测
GHSA-4q92 Low Symlink 绕过 deny 规则 isSymlink 用 realpathSync
GHSA-qgqw High find -exec 执行任意命令 isFindWithExec 新增检测
GHSA-q728 High ZSH clobber 任意写 isZshClobberBypass 新增检测
GHSA-vhw5 High 域名子域匹配绕过 isDomainAllowed 精确匹配 + . 前缀
GHSA-jh7p Moderate Trust 前泄漏 API Key isPreTrustNetworkPhase 推迟网络请求
GHSA-mmgp High Trust Dialog 绕过 hasUnsafeProjectConfig 新增配置检查
GHSA-xq4m High 命令验证绕过 containsCommandInjection 新增不可见字符检测

GitHub Security Advisories 页面上,Claude Code 已经公开了 22 个安全公告(三页),大部分由内部安全团队成员 ddworken 在 2025 年末到 2026 年初集中发布。

有一个很清晰的规律:几乎每一个 isXxx 安全函数的诞生背后都有一个具体的攻击。 安全规则不是一次性设计出来的,而是被一个个 CVE “雕刻”出来的。v0.x 只有一个简单的 isPathWithinCwd,到 v2.1 已经累积了十几个专项检查函数和一整套 OS 级沙箱。每个函数背后都是一次真实的渗透测试。


四、Feature Flags:Anthropic 的产品路线图

泄漏源码中的 Feature Flags 可能是对 Anthropic 伤害最大的部分——它们直接暴露了未发布的产品方向。

Flag 含义 状态
KAIROS 自主守护进程模式——后台常驻 + Dream 记忆整合 + 自主触发器 未发布
DAEMON 后台守护进程(KAIROS 子系统) 未发布
AGENT_TRIGGERS 自主触发器——文件变化、Git 事件、Cron 定时 部分上线
PROACTIVE 主动模式——Agent 不等指令,主动提出建议 已发布
BRIDGE_MODE IDE 桥接——VS Code / JetBrains 集成 已发布
VOICE_MODE 语音输入——语音对话交互 已发布
MONITOR_TOOL 监控工具——Agent 自我监控 部分上线
BUDDY 宠物伴侣——终端里的像素小狗 彩蛋
UNDERCOVER 隐身模式——清除 Anthropic 内部信息 内部使用

其中 KAIROS 最值得关注。

KAIROS 这个名字来自古希腊语 καιρός,意思是”恰当的时机”——不是 Chronos 式的线性时钟时间,而是做正确事情的最佳时刻。它包含三个子系统:Daemon Mode(后台常驻运行)、Auto Dream(睡眠时记忆整合)、Agent Triggers(自主感知环境变化并介入)。

Auto Dream 是其中最有意思的部分。Claude Code 在使用过程中会通过 src/memdir/ 积累项目记忆,经过十几个会话后记忆文件会变得臃肿。Auto Dream 借鉴了神经科学中的”记忆固化理论”——人类在睡眠时大脑会将短期记忆整合为长期记忆。Claude Code 做的事完全一样:在你不使用的时候,后台跑一个子 Agent 来整理记忆文件,合并重复条目、清理过时信息、重建索引。目标是把 MEMORY.md 控制在 200 行以内,保证下次会话的启动速度。

目前 Auto Dream 已经可以手动触发——在会话中说 “dream” 或 “consolidate my memory files”。但完整的 KAIROS 自主模式还没有对外开放。

这些 Feature Flags 被社区追踪得很紧。比如 2026 年 1 月有人在 v2.1.19 的 Flag 里发现了名为 TeammateTool(代号 Swarms)的多 Agent 系统,两周后 Anthropic 就正式发布了 Agent Teams 功能。正如 Hacker News 上有人评论的:**”对 Anthropic 来说,泄漏源码最大的损失不是代码本身,而是通过 Feature Flags 暴露了产品路线图。”**


五、彩蛋

Buddy——终端里的像素小狗

src/buddy/ 目录实现了一个名为 Pixel 的虚拟宠物系统。它是一只像素风格的小狗,会根据 Agent 的不同行为触发不同动画:

  • 空闲时——小动作,摇尾巴
  • 读文件时——看书动画
  • 跑 bash 命令时——敲键盘
  • 出错时——困惑/沮丧表情
  • 完成任务时——欢庆跳跃

目录结构里有 BuddyComponent.tsx(React/Ink 渲染组件)、buddyState.ts(心情和能量状态管理)、animations/(动画帧)、triggers.ts(触发条件映射)。

需要通过 BUDDY Feature Flag 开启,默认隐藏。从泄漏时间线来看,这大概率是 2025 年愚人节准备的彩蛋。

StickerRequestTool——问 Claude 要贴纸

当你在 Claude Code 里问”给我寄贴纸”之类的话时,会触发 StickerRequestTool,弹出一个终端表单收集你的收货地址,然后通过 Statsig 事件上报。理论上 Anthropic 会给你寄 Claude 贴纸——前提是这个 Flag 开着。

ThinkTool——Agent 的”草稿纸”

这不算彩蛋,但设计思路很有意思。ThinkTool 让模型”想一想”——把推理过程显式地记录到工具调用日志里,但不产生任何副作用。灵感来自 tau-bench 的研究。等于给 Agent 一张草稿纸,让它在动手之前先打个腹稿。


六、React + Ink:用 React 写终端 UI

Claude Code 的终端界面不是用 ncurses 或 blessed 这些传统方案写的,而是用 React + Ink 渲染到终端。

Ink 是一个把 React 组件树渲染为终端输出的库。声明式、组件化、状态管理用 hooks——和写 Web 前端几乎一模一样,只是输出目标从浏览器 DOM 变成了终端字符。

源码里有大约 140 个 Ink 组件,分布在 src/components/ 下。比如:

  • 对话消息渲染组件
  • 权限确认弹窗
  • 工具执行进度条
  • 代码 diff 高亮显示
  • Markdown 终端渲染
  • 文件树浏览器
  • 交互式表单(就是 StickerRequestTool 用的那个)
  • 全屏页面(Doctor 诊断、REPL、Resume 恢复会话)

为什么用 React 而不是传统终端 UI 库?我猜有几个原因:

第一,团队的前端工程能力可以直接复用。写 Ink 组件和写 React 组件的心智模型完全一致,不需要学新东西。

第二,声明式 UI 对 Agent 产品特别友好。Agent 的输出是流式的、状态频繁变化的——用命令式 UI 管理状态变更非常痛苦,声明式 UI 天然适合这种场景。

第三,组件化让 140 个 UI 模块可以独立开发和测试。代码里的 hooks/ 目录有大量自定义 Hook,比如权限检查的 useToolPermission、沙箱状态的 useSandboxStatus——安全逻辑和 UI 逻辑通过 Hook 清晰解耦。

一个有趣的细节:src/screens/ 下有 Doctor、REPL、Resume 三个”全屏页面”。Doctor 是一个诊断工具,检查环境配置是否正常;Resume 可以从上次中断的会话恢复。这些全屏页面用 Ink 的 <Box flexDirection="column"> 实现了类似于终端 TUI 应用的全屏布局,但代码读起来和 React Native 差不多。


写在最后

通读 Claude Code 的泄漏源码之后,最大的感受是:这不是一个”用 AI 写代码”的工具,这是一个精心设计的 Agent Runtime。

它不试图替代模型做决策。它不用精心设计的决策树限制模型的行为。它做的事情是:给模型提供工具、知识、上下文管理和安全边界——然后放手让模型自己判断。

用社区的话说:The model is the agent. The code is the harness.

代码是开放给所有人看的(虽然是意外),但产品的核心壁垒在服务端——在 Opus、Sonnet、Haiku 这些模型上。这大概也是为什么泄漏了 13 个月,Anthropic 虽然每次都快速删除,但并没有真正恐慌的原因。

复制遥控器很容易,但电视不在你手里。